﻿using System;
using TeamTasker.Contracts;
using System.Data;
using Spring.Data.Common;
using TeamTasker.Utils;
using Spring.Data;
using System.Collections.Generic;

namespace TeamTasker.Server.DataAccess
{
    public class UserDAO : BaseDAO<User>
    {
        public User Load(string login)
        {
            long userID = FindIdByLogin(login);
            if (userID > 0)
            {
                return Load(userID);
            }
            return null;
        }

        public override User Load(long id)
        {
            IDbParameters param = AdoTemplate.CreateDbParameters();
            param.Add("ID", SqlDbType.BigInt).Value = id;
            object res = AdoTemplate.QueryWithResultSetExtractor(CommandType.Text,
                @"select ID, Login, FullName, Email, LastLoginDate, IsActive  
                    from ApplicationUser where ID = @ID",
                new UserResultSetExtractor(),
                param);
            Validate.NotNull(res, "Loaded user object is null");
            return res as User;
        }

        public override void Save(User entity)
        {
            Validate.NotNull(entity, "User entity is illegal: null");            
            long userID = FindIdByLogin(entity.Login);

            IDbParameters param = AdoTemplate.CreateDbParameters();
            param.Add("Login", SqlDbType.NVarChar).Value = entity.Login;
            param.Add("FullName", SqlDbType.NVarChar).Value = entity.FullName;
            param.Add("Email", SqlDbType.NVarChar).Value = entity.Email;
            param.Add("Status", SqlDbType.Int).Value = (entity.IsActive ? 1 : 0);

            if (userID > 0)
            {
                param.Add("ID", SqlDbType.BigInt).Value = entity.ID;
                AdoTemplate.ExecuteNonQuery(CommandType.Text,
                    @"update ApplicationUser set Login = @Login, FullName = @FullName, 
                      Email = @Email, IsActive = @Status where ID = @ID", param);
            }
            else
            {
                param.Add("ID", SqlDbType.BigInt).Value = entity.ID = SeqGen.GetNextID();
                AdoTemplate.ExecuteNonQuery(CommandType.Text,
                    @"insert into ApplicationUser(ID, Login, FullName, Email, IsActive) 
                        values(@ID, @Login, @FullName, @Email, @Status)", param);
            }
        }

        public override void Delete(long id)
        {
            throw new NotSupportedException("User deletion is not supported; " +
                "only deactivation is available as a reaction on a user deletion in external repository.");   
        }

        public void DeactivateUser(long id)
        {
            SetUserStatus(id, false);
        }

        public void ActivateUser(long id)
        {
            SetUserStatus(id, true);
        }

        protected void SetUserStatus(long id, bool isActive)
        {
            IDbParameters param = AdoTemplate.CreateDbParameters();
            param.Add("ID", SqlDbType.BigInt).Value = id;
            param.Add("Status", SqlDbType.Int).Value = (isActive ? 1: 0);
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                "update ApplicationUser set IsActive = @Status where ID = @ID", param);
        }

        public IList<User> GetUserList()
        {
            return (IList<User>) AdoTemplate.QueryWithResultSetExtractor(CommandType.Text,
                "select ID, Login, FullName, Email, LastLoginDate from ApplicationUser where IsActive = 1",
                new UserListResultSetExtractor());
        }

        public void UpdateLoginTimestamp(string login) {
            IDbParameters param = AdoTemplate.CreateDbParameters();
            param.Add("Date", SqlDbType.DateTime).Value = DateTime.Now;
            param.Add("Login", SqlDbType.NVarChar).Value = login;
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                "update ApplicationUser set LastLoginDate = @Date where Login = @Login",
                param); 
        }

        /// <summary>
        /// Searches for a user ID by provided user login.
        /// </summary>
        /// <param name="login">login id</param>
        /// <returns>user id, if found, or 0</returns>
        protected long FindIdByLogin(string login)
        {
            IDbParameters param = AdoTemplate.CreateDbParameters();
            param.Add("Login", SqlDbType.NVarChar).Value = login;
            object userID = AdoTemplate.ExecuteScalar(CommandType.Text,
                "select ID from ApplicationUser where Login = @Login", param);
            if (userID != null) 
            {
                return (long) userID;
            }
            return 0;
        }
    }

    class UserResultSetExtractor : IResultSetExtractor
    {
        public object ExtractData(IDataReader reader)
        {
            if (reader.Read())
            {
                User user = new User();
                user.ID = reader.GetInt64(0);
                user.Login = reader.GetString(1);
                user.FullName = reader.GetString(2);
                user.Email = reader.GetString(3);
                user.LastLoginDate = reader.IsDBNull(4) ? DateTime.MaxValue : reader.GetDateTime(4);
                user.IsActive = reader.GetInt32(5) == 0 ? false : true;
                return user;
            }
            throw new ApplicationException("No data in data reader");
        }
    }

    class UserListResultSetExtractor : IResultSetExtractor
    {
        public object ExtractData(IDataReader reader)
        {
            IList<User> users = new List<User>();
            while (reader.Read())
            {
                User user = new User();
                user.ID = reader.GetInt64(0);
                user.Login = reader.GetString(1);
                user.FullName = reader.GetString(2);
                user.Email = reader.GetString(3);
                user.LastLoginDate = reader.IsDBNull(4) ? DateTime.MaxValue : reader.GetDateTime(4);
                user.IsActive = true; // query selects only active users
                users.Add(user);
            }
            return users;
        }
    }

}
